{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 读多写少场景的并发惯用法\n",
    "\n",
    "| 访问模式     | 典型场景拆分                             | 推荐方案（细化）                                             | 核心机制 / 关键术语                                          | 优点                                                         | 局限 / 注意事项                                              |\n",
    "| ------------ | ---------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |\n",
    "| **读多写少** | 数据量中等，写入偶尔发生；读线程延迟敏感 | ① **`shared_ptr` + Copy-On-Write / RCU (Read-Copy-Update) 风格**<br> • 读线程：获取局部 `shared_ptr` 快照（RAII 保生命周期）<br> • 写线程：`mutex` 保护 → `shared_ptr::unique()` 判断 → 必要时复制副本 → 原地修改或 `swap()` | - Copy-On-Write（写时复制）<br>- `shared_ptr::unique()` 判定唯一持有<br>- RCU 风格读：读操作无锁 / 极短锁<br>- RAII 自动管理生命周期 | - 读路径几乎无锁，延迟极低<br>- 写路径仅在“有人读”时复制，复制概率低<br>- 有现成示例可参考（如 `CopyOnWrite_test.cc`, `Customer.cc`） | - 写线程必须全程持锁，流程不可打断<br>- 大对象复制成本高，数据量巨大时写停顿会被放大 |\n",
    "| | 数据巨大（GB 级），复制成本高但写仍很少  | ② **版本化数据结构 / Append-Only + 指针翻转**<br> • 写线程构建新版本（结构化增量更新）<br> • 完成后用原子指针翻转到最新版本<br> • 读线程只读不可变快照 | - Immutable Data（不可变数据）<br>- 版本号管理 / 双缓冲（Double Buffering）<br>- 原子指针交换（`std::atomic` + `store`） | - 避免大规模复制，写停顿可控<br>- 可配合 Delta 日志进一步减少更新量 | - 实现复杂度高，需要定制容器或版本管理<br>- 老版本何时回收要有策略（引用计数或 epoch） |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Copy-On-Write（COW，写时复制）**\n",
    "\n",
    "- 核心思想：写线程先检查 `shared_ptr::unique()`；若有其他引用，则复制出独立副本再修改；若没有其他引用，就原地修改。\n",
    "- 这样既避免了读写冲突，又最大化减少不必要的拷贝。\n",
    "\n",
    "**Read-Copy-Update（RCU，读-复制-更新）风格**\n",
    "\n",
    "- 写线程在更新前复制出新版本，更新后交换指针；读线程始终使用旧的稳定快照。\n",
    "- `shared_ptr` 的引用计数在这里扮演“读者注册/注销”的角色，实现类似 RCU 的效果。\n",
    "\n",
    "**RAII（Resource Acquisition Is Initialization，资源获取即初始化）**\n",
    "\n",
    "- `shared_ptr` 本身利用 RAII 管理资源生命周期，保证读线程持有的快照在作用域结束前不会被释放，从而实现自动化的资源管理和线程安全。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关注“读多写少”场景的原因，主要源自业务负载特征和硬件现实：\n",
    "\n",
    "1. **大多数实际业务确实读多写少**\n",
    "   - Web 服务、内容分发、配置中心、在线分析、推荐系统等，都以读取现有数据为主，写入只是更新或新增。\n",
    "   - 各类缓存（Redis、Memcached、CDN 边缘节点）就是专为高比例读请求优化的典型例子。\n",
    "2. **读操作对用户体验更敏感**\n",
    "   - 读通常面向前台请求，直接影响延迟指标（p95/p99）。\n",
    "   - 写入往往可以异步或排队处理，而读取需要“即时”响应。\n",
    "3. **简单互斥在读多场景下效率极差**\n",
    "   - 如果读和写都用同一把互斥锁，写虽然少，但会阻塞大量读，造成高延迟。\n",
    "   - 因此工程上会投入更多精力把读路径剥离成“无锁”或“极低锁开销”的方案。\n",
    "4. **硬件架构给读优化更多空间**\n",
    "   - CPU 缓存层次和 NUMA 架构下，只读数据可高效共享，避免频繁缓存失效。\n",
    "   - 写会触发缓存一致性消息广播和 TLB 刷新，成本更高，控制写频率能减轻系统负担。\n",
    "5. **读优化往往带来系统可扩展性**\n",
    "   - 通过 Copy-On-Write、RCU、双缓冲等手段，读线程几乎无锁访问，吞吐能随着 CPU 核数线性增长。\n",
    "   - 写少意味着可以接受一定“重”操作（复制一份、版本切换），从而换取读端的轻量级并发。\n",
    "6. **数据副本和缓存策略天然针对读**\n",
    "   - 读多写少让 CDN、前端缓存、数据库只读副本等复制策略更容易实施。\n",
    "   - 多副本能平衡读负载，而写频繁会导致同步复杂、开销大。\n",
    "7. **一致性策略容易围绕读进行权衡**\n",
    "   - 写少时，可通过延迟写、批量写、异步复制等手段隐藏成本。\n",
    "\n",
    "- 读路径可以保持一致性强或延迟小，从而满足核心体验。\n",
    "\n",
    "1. **优化读路径的收益巨大**\n",
    "   - 如果 90% 以上的请求是读，即便单次读延迟减少 10%，整体性能改进也非常显著。\n",
    "   - 高频场景优化的边际价值更高，工程团队更愿意投入资源。\n",
    "\n",
    "**结论**：读多写少是绝大多数生产系统的常态。为了在多核和分布式环境中获得更好的吞吐与延迟表现，我们会优先设计各种无锁、低锁、版本化的读优化策略，并在写操作较少的前提下承担必要的复杂度与资源开销。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 读写近似场景的并发惯用法\n",
    "\n",
    "| 访问模式     | 典型场景拆分                     | 推荐方案（细化）                                             | 核心机制 / 关键术语                                      | 优点                                                       | 局限 / 注意事项                                              |\n",
    "| ------------ | -------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------ |\n",
    "| **读写近似** | 冲突概率不算高，逻辑简单优先     | ① **细粒度互斥锁**<br> • 按数据块划分多把 `mutex`<br> • 读写都用互斥锁，减少热点冲突 | - 粗易控的 Critical Section 设计                         | - 实现简单，易定位问题<br>- 读写语义清晰                   | - 锁划分不合理会导致竞争仍然激烈<br>- 复杂度随数据结构增长   |\n",
    "| | 读与写都需较高并发，写占比约 50% | ② **读写锁 (RWLock) 或升级读写锁**<br> • 读线程：加读锁并发执行<br> • 写线程：加写锁独占 | - Reader-Writer Lock<br>- “写优先 / 读优先”策略可调      | - 读多时并发性能不错<br>- API 广泛，适合快速迭代           | - 写方可能饥饿（多数 RWLock 写优先）<br>- 向下升级/升级逻辑需谨慎，易死锁 |\n",
    "|  | 冲突概率低，但读写都可能同时发生 | ③ **乐观锁（Optimistic Locking）+ 版本验证**<br> • 读：先读取，再校验版本号或校验和<br> • 写：失败则重试 | - Compare-And-Swap (CAS)<br>- MVCC（多版本并发控制）理念 | - 读路径极快，冲突少时吞吐高<br>- 写冲突时自动退避重试     | - 冲突频繁场景下退化为忙等，成本高<br>- 需要额外版本字段管理一致性 |\n",
    "|  | 高性能要求，团队有经验           | ④ **Lock-Free / 完整 RCU + epoch 回收**<br> • 读：无锁遍历，使用 epoch 或 Hazard Pointer 防止提前释放<br> • 写：构建新结构后原子替换，旧版本延迟回收 | - Lock-Free 数据结构<br>- Hazard Pointer / Epoch GC      | - 理论性能最佳，读写都可无阻塞<br>- 适合高吞吐、低延迟需求 | - 实现和调试成本极高<br>- 内存管理复杂，需周密测试           |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 写多读少场景的并发惯用法\n",
    "\n",
    "| 访问模式     | 典型场景拆分                   | 推荐方案（细化）                                             | 核心机制 / 关键术语                                    | 优点                                                         | 局限 / 注意事项                                              |\n",
    "| ------------ | ------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |\n",
    "| **写多读少** | 写线程频繁，读偶尔             | ① **粗粒度互斥锁**<br> • 所有修改串行化，读写均加同一把锁    | - 简单 Mutex                                           | - 实现最直接，便于验证正确性<br>- 缓存命中率最高（单线程写） | - 读线程等待时间长，需要评估能否接受                         |\n",
    "|   | 多写者访问巨大表格             | ② **分段锁 / 分片数据结构（Sharding）**<br> • 根据 key 哈希分布到多个独立 `mutex`<br> • 写操作定位到对应分段加锁 | - Sharding（分片）<br>- 分段锁（Segmented Lock）       | - 写并发能力增强，热点分散<br>- 读写都保持简单互斥语义       | - 热点分片依然会竞争，需要监控统计<br>- 分段数量和负载均衡需调优 |\n",
    "|   | 需要严格顺序写入，读延迟可接受 | ③ **单写线程 + 消息/任务队列**<br> • 其他线程将写请求排入队列<br> • 专门写线程按序执行更新<br> • 读线程按需读取最新快照 | - Single Writer Principle<br>- Producer-Consumer Queue | - 易保证一致性和顺序<br>- 写线程利用缓存最佳，逻辑集中       | - 写线程成为单点瓶颈，需监控 back pressure<br>- 读线程读取快照需考虑延迟同步 |\n",
    "|   | 写负载高但可批量处理           | ④ **批量写 (Batching) + 周期性 `swap`**<br> • 写请求先进入 buffer<br> • 定时或计数触发批量应用<br> • 读线程读取上一次提交的快照 | - Batch Processing<br>- Double Buffer + Swap           | - 平均每次写锁开销降低<br>- 可减少磁盘或网络写入次数         | - 写请求延迟增加，需与业务时限匹配<br>- 适合可容忍轻微滞后的一致性要求 |"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "name": "C++17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
